﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Windows.Markup;

using agree.schema;

using alib;
using alib.Character;
using alib.Debugging;
using alib.Enumerable;
using alib.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// a TDL token
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[ContentProperty("Form")]
	[DebuggerDisplay("{this.ToString(),nq}")]
	public class TdlTok : IIdentity, ρρText
	{
		public static readonly TdlTok Null = new TdlTok(FilePos.None, TdlTok.Type.Null, "Null");
		public static readonly TdlTok Eof = new TdlTok(FilePos.None, TdlTok.Type.EOF, "Eof");
		public static readonly String ReplaceToken = "${";

		[Flags]
		public enum Type
		{
			Identifier /*				*/ = 0x00000001,
			String  /*					*/ = 0x00000002,
			Integer /*					*/ = 0x00000004,	// (123)
			Tag  /*						*/ = 0x00000008,	// #...
			XMapTag  /*					*/ = 0x00000010,	// @...
			// single character tokens
			Ampersand  /*				*/ = 0x00000100,	// &
			Dot  /*						*/ = 0x00000200,	// .
			Comma  /*					*/ = 0x00000400,	// ,
			SquareOpen  /*				*/ = 0x00000800,	// [
			SquareClose  /*				*/ = 0x00001000,	// ]
			AngleOpen  /*				*/ = 0x00002000,	// <
			AngleClose  /*				*/ = 0x00004000,	// >
			// atomic 2-character tokens (can contain whitespace because of matrix.tdl)
			Append  /*					*/ = 0x00008000,	// :+
			Define  /*					*/ = 0x00010000,	// :=   : =   :<
			DifferenceListOpen  /*		*/ = 0x00020000,	// <!   < !
			DifferenceListClose  /*		*/ = 0x00040000,	// !>   ! >
			Ellipsis  /*				*/ = 0x00080000,	// ...
			// morphology specification also uses inherited type
			Morphology  /*				*/ = 0x00100000,
			// regular expression used by e.g. token mapping rule
			RegEx /*					*/ = 0x00200000,
			RegExRepl /*				*/ = 0x00400000,

			Comment /*					*/ = 0x01000000,

			/// special tokens to report parser state
			Null /*						*/ = 0x20000000,
			EOF /*						*/ = 0x40000000,
			None /*						*/ = 0,
			TokMap_Empty /*				*/ = None,
			TokMap_Comma /*				*/ = Comma,
			TokMap_SqCl /*				*/ = SquareClose,
			TokMap_SqCl_Comma /*		*/ = SquareClose | Comma,
			TokMap_AngCl /*				*/ = AngleClose,
			TokMap_AngCl_Comma /*		*/ = AngleClose | Comma,
			TokMap_AngCl_Comma_Dot /*	*/ = AngleClose | Comma | Dot,
			TokMap_DlsCl_Comma /*		*/ = DifferenceListClose | Comma,
			TokMap_AnyOpen /*			*/ = SquareOpen | AngleOpen | DifferenceListOpen,
			TokMap_AnyClose /*			*/ = SquareClose | AngleClose | DifferenceListClose,
		};


		/// <summary>
		/// As you can see, this class is a very poor example of a proper pipeline trace implementation
		/// </summary>
		public TdlTok(FilePos fp, TdlTok.Type type, String text)
		{
			this.FilePos = fp;
			this.type_id = type;
			this.text = text;
		}

		public TdlTok(String form)
			: this(FilePos.None, TdlTok.Type.None, form)
		{
		}

		public readonly TdlTok.Type type_id;
		public readonly FilePos FilePos;
		[AccessedThroughProperty("Form")]
		//readonly
		public String text;


		///////////////////////////////////////////////////////////////////////
		/// 
		public String Text
		{
			get { return this.text; }
			set { throw not.valid; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		public override String ToString()
		{
			switch (type_id)
			{
				case TdlTok.Type.Identifier:
				case TdlTok.Type.Tag:
				case TdlTok.Type.XMapTag:
					return " " + text + " ";
				case TdlTok.Type.String:
					return " \"" + text + "\" ";
				case TdlTok.Type.Integer:
					return " (" + text + ") ";
				case TdlTok.Type.Ampersand:
					return "&";
				case TdlTok.Type.Dot:
					return ".";
				case TdlTok.Type.Comma:
					return ",";
				case TdlTok.Type.SquareOpen:
					return "[";
				case TdlTok.Type.SquareClose:
					return "]";
				case TdlTok.Type.AngleOpen:
					return "<";
				case TdlTok.Type.AngleClose:
					return ">";
				case TdlTok.Type.Append:
					return ":+";
				case TdlTok.Type.Define:
					return ":=";			// also, :<
				case TdlTok.Type.DifferenceListOpen:
					return "<!";
				case TdlTok.Type.DifferenceListClose:
					return "!>";
				case TdlTok.Type.Ellipsis:
					return "...";
				case TdlTok.Type.RegEx:
					return " regex[" + text + "] ";
				case TdlTok.Type.RegExRepl:
					return " repl[" + text + "] ";
				case TdlTok.Type.EOF:
					return "EOF";
				case TdlTok.Type.Null:
					return "(null)";
				case TdlTok.Type.None:
					return "None";
			}
			return "??";	//t.ToString(), Name == null ? String.Empty : Name
		}

		public IIdentity Trace { get { return SysObj.Instance; } }
	};

#if false
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	/// Not sure if we'll end up using this. Don't really see any point to getting an early start on trying
	/// to build and compile the token mapping Regex when it's more likely we could discover some obscure
	/// fact that relis on some obscure aspect of the rule or TFS that won't be available yet.
	/// 
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class TdlTokRegex : TdlTok
	{
		public TdlTokRegex(FilePos fp, String form)
			: base(fp, Type.RegEx, form)
		{
		}
		public Regex rxegex;
		public override String ToString() { return " (regex)[" + Name + "] "; }
	};
#endif


	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	/// For morphological regexes, the TDL token is extended to carry the regexes
	/// 
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class TdlTokMorphology : TdlTok
	{
		public TdlTokMorphology(FilePos fp, String s_value)
			: base(fp, Type.Morphology, s_value)
		{
			this.subrules = new List<MorphologySubrule>();
		}
		public readonly List<MorphologySubrule> subrules;
	};


	[DebuggerDisplay("{ToString(),nq}")]
	public struct MorphologySubrule
	{
		public Regex regex;
		public String replace;

		public Regex rev_regex;
		public String rev_replace;

		public override string ToString()
		{
			return String.Format("{0} {1} ↔ {2} {3}", regex, replace, rev_regex, rev_replace);
		}
	};


	public sealed class TdlTokPrefix : TdlTokMorphology
	{
		public TdlTokPrefix(FilePos fp, String name)
			: base(fp, name)
		{
		}

		public override String ToString() { return " %prefix " + Text + " "; }
	};


	public sealed class TdlTokSuffix : TdlTokMorphology
	{
		public TdlTokSuffix(FilePos fp, String name)
			: base(fp, name)
		{
		}

		public override String ToString() { return " %suffix " + Text + " "; }
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// generalized collection of TDL tokens
	/// A stream of TDL tokens that might originate from a TFS root. Or a list of TDL tokens for any other purpose.
	/// In fact this is also used to maintain a collection of parent (identifier) types, prior to building the
	/// type hierarchy.
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class TdlTokenList : List<TdlTok>, IAddRangeList<TdlTok>
	{
		public TdlTokenList()
			: base(8)
		{
		}
		public override String ToString()
		{
			return this.StringJoin("");
		}
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	/// Several instances of the preceding token lists are then grouped together according to their roles in 
	/// constraining different paths in the same typed feature structure. In addition to the list of tokens, a
	/// single TdlTok is stored as the distingusihed type name and/or instance name for the emerging structure.
	/// I can't recall why this is separate from the class that follows, 
	/// especially since the latter always instantiates a singleton of this one. I always find this part of agree
	/// to be a bit confusing, but concerted repair efforts always seem to get bogged down.
	///
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class ConsDefs : List<TdlTokenList>, IAddRangeList<TdlTokenList>, IActiveObj<TdlTok>,
		ρρTdlType
	{
		public ConsDefs(TdlTok κIdent, TdlType tdl_type, int src_seq_id)
		{
			this.identifier = κIdent;
			this.tdl_type = tdl_type;
			this.src_seq_id = src_seq_id;
		}
		public ConsDefs(String name, TdlType tdl_type = TdlType.Type)
			: this(new TdlTok(name), tdl_type, 0)
		{
		}

		public readonly TdlType tdl_type;
		public readonly TdlTok identifier;
		public int cch;
		public int src_seq_id;


		///////////////////////////////////////////////////////////////////////
		/// 
		public TdlType TdlType
		{
			get { return this.tdl_type; }
			set { throw not.valid; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////


		public override String ToString()
		{
			String s;
			s = String.Format("id:{0:X8} ----- [{1}] --------- {2} cch:{3}" + Environment.NewLine,
				src_seq_id,
				identifier.ToString().Trim(),
				tdl_type,
				cch);
			foreach (TdlTokenList expr in this)
				s += expr.ToString() + Environment.NewLine;
			return s;
		}

		IDisposable IObservable<TdlTok>.Subscribe(IObserver<TdlTok> obs)
		{
			foreach (var ttl in this)
				foreach (var tok in ttl)
					obs.OnNext(tok);
			obs.OnCompleted();
			return EmptyDisposable.Instance;
		}

		IIdentity IIdentity.Trace { get { return null; } }
		IRtParent IRtChild.Parent { get { return null; } }
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// As TDL processing progresses, here we represent the assignment of raw TDL token streams to eventual TFSes 
	/// Still a fairly literal representation of the TFS as authored; little interpretation of the tokens--apart
	/// from precisely what's necessary to achieve fully correct per-TFS groupings--has been applied. Note however
	/// that the (Grammar-matrix-friendly) append operator does get fully analyzed here because agree currently makes 
	/// no provision for reconsidering these groupings. Fortunately, no such issue has plagued the design.
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class IdentifierTokenGroup : identity_base
	{
		public IdentifierTokenGroup(TdlType tdl_type, TdlTok κ, int src_seq_id, bool f_append)
			: base(κ)
		{
			this.f_append = f_append;
			this.κκParents = new TdlTokenList();
			this.constraints = new ConsDefs(κ, tdl_type, src_seq_id);
		}

		public readonly bool f_append;			// false for :=  true for :+

		public String top_comment;				// conjoined top-level comment

		public readonly TdlTokenList κκParents;	// conjoined parents

		/// zero or more Tdl token streams of arbitray length, each of which correspond to one rooted constraint 
		/// path for the putative structure. If 'putative' seems like an odd word choice, one need only reflect that
		/// nearly 100% of the structures that TDL would deem lexically correct are yet hopelessly flawed on formal 
		/// bases.
		public ConsDefs constraints;

		public String Render()
		{
			try
			{
				String ret = String.Format("Tdl definition for \"{0}\"" + Environment.NewLine, constraints.identifier.Text);
				if (κκParents != null && κκParents.Count > 0)
					ret += String.Format("{0} Parent(s):\t{1}" + Environment.NewLine,
											κκParents.Count,
											κκParents.Select(e => e.Text).StringJoin(" "));

				int c = constraints.Count;
				if (c > 0)
				{
					ret += String.Format("{0} Tdl Statement(s):" + Environment.NewLine, c);
					ret += constraints.ToString();
				}
				if (top_comment != null)
					ret += String.Format("Comment:\t{0}" + Environment.NewLine, top_comment);
				return ret.CondenseSpaces();
			}
			catch (Exception ex)
			{
				return ex.ToString();
			}
		}
	};

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// a version of the above which can transport a list of morphology affixing RegEx's
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed class MorphologyIdentifierTokenGroup : IdentifierTokenGroup
	{
		public MorphologyIdentifierTokenGroup(TdlType tdl_type, TdlTok κ, int id, bool f_append)
			: base(tdl_type, κ, id, f_append)
		{
			this.morph_subrules = new List<MorphologySubrule>();
		}
		public readonly List<MorphologySubrule> morph_subrules;	// morphology subrules
	};
}
